package philbot;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;


/*
 * This class is supposed to be used to run regression testing and simulations
 * I have set up a box to grab the source, build it, and run the main method from this, once every hours
 */
public class PhilBotTest {

	//private static int depthParam = 2;
	
	//only tests that equal things are equal 
	public static boolean testCPositionEquality(boolean loud){
		boolean cond = true;
		AbstractPosition[] canidates = new AbstractPosition[100];
		canidates[0] = genSmallPosition();
		for(int i = 0; i < canidates.length-1; i++){
			canidates[i+1] = (canidates[i]).getChildren().get(0);
		}
		AbstractPosition t = canidates[0];
		for(int i = 0; i < canidates.length; i++){
			cond = cond & t.equals(canidates[i]);
			t = t.getChildren().get(0);
		}
		/*
		for(int i = 0; i < canidates.length; i++){
			for(int j = i+1; (j < canidates.length)&&cond; j++){
				cond = cond & !((canidates[j]).equals(canidates[i]));
				if(loud && !cond){
					System.out.println("j is " + j);
					System.out.println((canidates[j]));
					System.out.println("i is " + i);
					System.out.println((canidates[i]));
				}
			}
		}
		*/
		return cond;
	}
	
	//
	public static boolean testPVTable(boolean loud){
		/*
		 * It is always amusing when you discover that a bug in a test
		 */
		AbstractPosition[] canidates= new AbstractPosition[100];
		Random r = new Random();
		canidates[0] = genSmallPosition();
		PVTable pvtable = new PVTable();
		for(int i = 0; i < canidates.length-1; i++){
			AbstractPosition p = canidates[i];
			List<AbstractPosition> kids = p.getChildren();
			int l = kids.size()-1;
			int q = (l > 1) ? r.nextInt(l-1) : 0;
			canidates[i+1] = kids.get(q);
		}
		int tryNofurther = canidates.length;
		for(int i = 0; i < tryNofurther; i++){
			for(int j = i+1; j < tryNofurther; j++){
				if ((canidates[i]).equals(canidates[j])){
					tryNofurther = j - 1;
					/*
					if(loud){
						System.out.println("equality at " + i);
						System.out.println(canidates[i]);
						System.out.println("and " + j);
						System.out.println(canidates[j]);
					}
					*/
				}
			}
		}
		if(loud){
			System.out.println("tring up to " + tryNofurther);
		}
		for(int i = 0;  i < tryNofurther -1; i++){
			AbstractPosition p = canidates[i];
			pvtable.setPV(10, p , canidates[i+1]);
		}
		boolean cond = true;
		for(int i = 0; i < tryNofurther-1; i++){
			if(loud)
				System.out.println("At: " + i + " cond is: " + cond);
			cond = cond && (canidates[i+1].equals(pvtable.getPV(canidates[i])));
			if(loud){
				if(!cond){
					System.out.println("v at " + i);
					System.out.println(canidates[i]);
					System.out.println("refulation at " + i);
					System.out.println(pvtable.getPV(canidates[i]));
					System.out.println("should be " + i);
					System.out.println(canidates[i+1]);
				}
			}
		}
		return cond;
	}

	public static void playNegaMax(int depth){
		/*
		boolean gameOver = false;
		AbstractPosition p = genSmallPosition();
		PhilBotCore player = new PhilBotCore(new TransTable());
		for(int i = 0; !gameOver; i++){
			player.pvtable = new PVTable();
			System.out.println("negamax test");
			System.out.println("it is the " + i + " position of the game");
			System.out.println(p);
			player.negaMax(depth, p);
			AbstractPosition r = player.pvtable.getPV(p);
			p = r;
			gameOver = p.isVictory();
		}
		System.out.println("win!");
		System.out.println(p);
		*/
	}
	
	public static void playAB(int depth){
		boolean gameOver = false;
		AbstractPosition p = genSmallPosition();
		PhilBotCore player = new PhilBotCore(new TransTable());
		for(int i = 0; !gameOver; i++){
			player.pvtable = new PVTable();
			System.out.println("alpha beta test");
			System.out.println("it is the " + i + " position of the game");
			System.out.println(p);
			player.alphaBeta(depth, p, Integer.MIN_VALUE, Integer.MAX_VALUE, Evaluator.defaultEvaluator(),depth -2);
			AbstractPosition r = player.pvtable.getPV(p);
			p = r;
			gameOver = p.isVictory();
		}
		System.out.println("win!");
		System.out.println(p);
	}
	/*
	public static void playMEAB(int depth){
		boolean gameOver = false;
		AbstractPosition p = genSmallPosition();
		PhilBotCore player = new PhilBotCore(new TransTable());
		for(int i = 0; !gameOver; i++){
			player.pvtable = new PVTable();
			System.out.println("memory enhanced alpha beta test");
			System.out.println("it is the " + i + " position of the game");
			System.out.println(p);
			player.meAlphaBeta(depth, p, Integer.MIN_VALUE, Integer.MAX_VALUE, depth-2);
			AbstractPosition r = player.pvtable.getPV(p);
			p = r;
			gameOver = p.isVictory();
		}
		System.out.println("win!");
		System.out.println(p);
	}
	
	
	public static void playID(int depth){
		boolean gameOver = false;
		AbstractPosition p = genSmallPosition();
		PhilBotCore player = new PhilBotCore(new TransTable());
		for(int i = 0; !gameOver; i++){
			player.pvtable = new PVTable();
			System.out.println("iterative deepning");
			System.out.println("it is the " + i + " position of the game");
			System.out.println(p);
			player.iterativeDeepen(depth, p, Integer.MIN_VALUE, Integer.MAX_VALUE);
			AbstractPosition r = player.pvtable.getPV(p);
			p = r;
			gameOver = p.isVictory();
		}
		System.out.println("win!");
		System.out.println(p);
		double average = ((double) player.burnCount)/((double) player.nodeCount);
		System.out.println("the average position of breakage is" + average);
	}
	*/
	//
	public static SmallPosition genSmallPosition(){
		Random r = new Random();
		boolean[][] table = new boolean[9][11];
		int[][] hashVals = new int[9][9];
		for(int i = 0; i < 9; i++){
			for(int j = 0; j  < 9; j++){
				hashVals[i][j] = r.nextInt();
			}
		}
		return new SmallPosition(table,4,4,States.START,true,hashVals);
	}
	public static NBNPosition genNBNPosition(){
		Random r = new Random();
		boolean[][] table = new boolean[19][21];
		int[][] hashVals = new int[19][19];
		for(int i = 0; i < 19; i++){
			for(int j = 0; j  < 19; j++){
				hashVals[i][j] = r.nextInt();
			}
		}
		return new NBNPosition(table,9,9,States.START,true,hashVals);
		
	}
	
	public static String runtimes(PhilBotCore.SearchAlg alg,int startat, int stopat, double multiple, int av){
		StringBuilder b = new StringBuilder();
		BotHolder.Builder builder = new BotHolder.Builder();
		builder.depth(Integer.MAX_VALUE - 1).mode(PhilBotCore.SearchMode.ID).alg(alg);
		int i = startat;
		while(i < stopat){
			BotHolder b1 = builder.count(i).build();
			BotHolder b2 = builder.build();
			SingleGame game = new SingleGame(b1, b2, genSmallPosition(), 1000);
			game.play(false);
			b.append(i).append(" , ").append(game.getWinner()).append(" , ").append(game.getAverageTimeBothPlayers()).append("\n");
			i = (int) (multiple*(double)i);
			i += av;
		}
		return b.toString();
	}
	
	public static String runtimesOfList(PhilBotCore.SearchAlg alg, int startat, int stopat, double mult, int av, List<AbstractPosition> pos){
		StringBuilder b = new StringBuilder();
		BotHolder.Builder builder = new BotHolder.Builder();
		builder.depth(Integer.MAX_VALUE - 1).mode(PhilBotCore.SearchMode.ID).alg(alg);
		int i = startat;
		final double fac = pos.size();
		System.out.println("Fac is " + fac);
		while(i < stopat){
			System.out.println("I is " + i);
			BotHolder b1 = builder.count(i).build();
			long timesum = 0;
			long startTime = System.nanoTime();
			for(AbstractPosition p: pos){
				b1.play(p);
			}
			long endTime = System.nanoTime();
			timesum += (endTime - startTime);
			double average = ((double)timesum)/fac;
			b.append(i).append(" , ").append(average/1000).append(" , ").append((average/((double)i))/1000).append("\n");
			i = (int) (mult*(double)i);
			i += av;
		}
		return b.toString();
	}
	public static String runtimesOfListDepth(PhilBotCore.SearchAlg alg, int startat, int stopat, List<AbstractPosition> pos){
		StringBuilder b = new StringBuilder();
		BotHolder.Builder builder = new BotHolder.Builder();
		builder.count(Integer.MAX_VALUE - 1).mode(PhilBotCore.SearchMode.ID).alg(alg);
		int i = startat;
		final double fac = pos.size();
		System.out.println("Fac is " + fac);
		while(i < stopat){
			System.out.println("I is " + i);
			BotHolder b1 = builder.depth(i).build();
			long timesum = 0;
			long startTime = System.nanoTime();
			for(AbstractPosition p: pos){
				b1.play(p);
			}
			long endTime = System.nanoTime();
			timesum += (endTime - startTime);
			double average = ((double)timesum)/fac;
			b.append(i).append(" , ").append(average/1000).append("\n");
			i++;
		}
		return b.toString();
	}
	
	public static String nodesCountsOfList(PhilBotCore.SearchAlg alg, int startat, int stopat, List<AbstractPosition> pos){
		StringBuilder b = new StringBuilder();
		BotHolder.Builder builder = new BotHolder.Builder();
		builder.count(Integer.MAX_VALUE - 1).mode(PhilBotCore.SearchMode.ID).alg(alg);
		int i = startat;
		double fac = pos.size();
		while(i < stopat){
			System.out.println("node depth " + i);
			BotHolder b1 = builder.depth(i).build();
			long sumOfNodes = 0;
			for(AbstractPosition p: pos){
				b1.play(p);
				sumOfNodes += b1.getNodeCunt();	
			}
			double averageCount = ((double) sumOfNodes)/fac;
			double averageFanOut = Math.pow(averageCount, 1 / ((double) i));
			b.append(i).append(" , ").append(averageCount).append(" , ").append(averageFanOut).append(" \n");
			i++;
		}
		return b.toString();	
	}
	
	public static void generateNumbers(SingleGame game){
		System.out.println("1");
		String abNodes = nodesCountsOfList(PhilBotCore.SearchAlg.ABPRUNING, 2, 3, game.getPositions());
		System.out.println("2");
		String negaNodes = nodesCountsOfList(PhilBotCore.SearchAlg.NEGAMAX, 2, 6, game.getPositions());
		System.out.println("3");
		String memoryABNodes = nodesCountsOfList(PhilBotCore.SearchAlg.MEMORYAB, 2, 7, game.getPositions());
		System.out.println("4");
		String timeNodeNega = PhilBotTest.runtimesOfList(PhilBotCore.SearchAlg.NEGAMAX, 100, 20000, 1.5, 0, game.getPositions());
		System.out.println("5");
		String timeNodeMEM = PhilBotTest.runtimesOfList(PhilBotCore.SearchAlg.MEMORYAB, 100, 20000, 1.5, 0, game.getPositions());
		System.out.println("6");
		String timeDepthNEGA = PhilBotTest.runtimesOfListDepth(PhilBotCore.SearchAlg.NEGAMAX, 2, 3, game.getPositions());
		System.out.println("7");
		String timeDepthAB = PhilBotTest.runtimesOfListDepth(PhilBotCore.SearchAlg.ABPRUNING, 2, 6, game.getPositions());
		System.out.println("8");
		String timeDepthMEM = PhilBotTest.runtimesOfListDepth(PhilBotCore.SearchAlg.MEMORYAB, 2, 7, game.getPositions());
		System.out.println("negamax without hashing node count data:");
		System.out.println(negaNodes);
		System.out.println("ab pruning without hashing node count data:");
		System.out.println(abNodes);
		System.out.println("ab pruning with exact hashing node cound data:");
		System.out.println(memoryABNodes);
		System.out.println("time per node negamax:");
		System.out.println(timeNodeNega);
		System.out.println("time per node ab pruning with hashing:");
		System.out.println(timeNodeMEM);
		System.out.println("time for depth negamax:");
		System.out.println(timeDepthNEGA);
		System.out.println("time for depth ab pruning without hashing:");
		System.out.println(timeDepthAB);
		System.out.println("time for depth ab pruning with exact hashing:");
		System.out.println(timeDepthMEM);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("CPosition Equality Test " +  testCPositionEquality(true));
		System.out.println("PVTable Test " + testPVTable(false));
		//playNegaMax(3);
		//playAB(3);
		//playMEAB(3);
		//playID(4);
		BotHolder b1 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.ABPRUNING).build();
		BotHolder b2 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.ABPRUNING).build();
		SingleGame game = new SingleGame(b1, b2, genSmallPosition(), 100);
		/*game.play(false);
		System.out.println(game.getWinner() + " in " + game.getAverageTimeBothPlayers() + " average per move");
		b1 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.MEMORYAB).build();
		b2 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.MEMORYAB).build();
		game = new SingleGame(b1,b2,genNBNPosition(), 100);
		game.play(false);
		System.out.println("NBN " + game.getWinner() + " in " + game.getAverageTimeBothPlayers() + " average per move");
		//runtimes(PhilBotCore.SearchAlg.ABPRUNING, 10, 2000, 1.1, 0);
		System.out.println("hi");
		b1 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.ABPRUNING).build();
		b2 = (new BotHolder.Builder()).depth(3).alg(PhilBotCore.SearchAlg.ABPRUNING).build();
		game = new SingleGame(b1, b2, genSmallPosition(), 100);*/
		game.play(false);
		generateNumbers(game);
	}

}
